package com.UnionPayCenter.wechat;

import com.UnionPayCenter.entity.ShopInfoEntity;
import com.UnionPayCenter.entity.UserEntity;
import com.UnionPayCenter.util.HttpClient;
import com.UnionPayCenter.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.*;

/**
* @ClassName: WechatPayUtil
* @Description:  微信支付
* @Author: bite
* @Date: 2019/6/1
*/
@Slf4j
@Component
public class WechatPayUtil {
    //微信支付下单成功
    public static final String Y = "0";
    //微信支付下单失败
    public static final String N = "1";
    // 微信支付回调方法
    private static String WECHAT_BACK = "/wechatPayBack";
    // 微信支付统一接口(POST)
    public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /** 
    * @Description: 微信支付下单
    * @Param:   domainUrl：ConfService -getDomainUrl()获取
     *          shopInfo：商铺配置信息 ShopService - wechatPayGetShopInfo() 获取
     *          user：会员信息 UserService - wechatPayGetUser() 获取
     *          orderId： 订单号
     *          price：价格（分）
     *          desc：商品或支付单简要描述
    * @return:  
    * @Author: bite 
    * @Date: 2019/6/1 
    */ 
    public static Map<String,Object>  pay(String domainUrl, ShopInfoEntity shopInfo, UserEntity user, String orderId, double price, String desc) {
        Map<String,Object> map = new HashMap<String,Object>();
        try{

            Map<String, String> wechatMap = new TreeMap<String, String>();
            wechatMap.put("appid", shopInfo.getAppId());
            wechatMap.put("mch_id", shopInfo.getMerchantId());
            wechatMap.put("openid", user.getOpenid());
            // 商品或支付单简要描述
            wechatMap.put("body", desc);
            // 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
            // wechatMap.put("attach", "");
            // 随机字符串，不长于32位。推荐随机数生成算法
            wechatMap.put("nonce_str", WechatPayUtil.getRandomNumberString(5));
            // 接收微信支付异步通知回调地址
            wechatMap.put("notify_url", domainUrl + WechatPayUtil.WECHAT_BACK);
            // 商户系统内部的订单号,32个字符内、可包含字母
            wechatMap.put("out_trade_no", orderId);
            // 用户端实际ip
            InetAddress netAddress = InetAddress.getLocalHost();
            wechatMap.put("spbill_create_ip", netAddress.getHostAddress());
            // 订单总金额
            wechatMap.put("total_fee", new BigDecimal(price).intValue()+"");
            // 支付类型
            wechatMap.put("trade_type", "JSAPI");
            // 签名
            String sign = wechatSign(wechatMap, shopInfo.getMerchantSecret());
            wechatMap.put("sign", sign);

            // 微信Xml参数
            String params = WechatPayUtil.getXmlData(wechatMap);
            // 调用微信下单接口
            HttpClient httpClient = new HttpClient();
            httpClient.setUrl(WechatPayUtil.UNIFIED_ORDER_URL);
            httpClient.setParams(params);
            String result = httpClient.post();
            JSONObject resultJson = WechatPayUtil.getJsonData(result).getJSONObject("xml");

            // 返回失败
            String return_code = resultJson.getString("return_code");
            if (!return_code.equals("SUCCESS")) {
                String err_code_des = resultJson.getString("err_code_des");
                map.put("code", WechatPayUtil.N);
                map.put("message", err_code_des);
                log.error(String.format("微信下单失败，用户：%s-%s， 原因：%s", user.getUserId(), user.getMobile(), err_code_des));
                return map;
            }else{
                // app签名
                Map<String, String> appMap = new TreeMap<String, String>();
                Calendar calendar = Calendar.getInstance();
                Date date = calendar.getTime();
                long timestamp = date.getTime() / 1000;
                String nonceStr = MD5Util.MD5Encode(WechatPayUtil.getRandomNumberString(5), "utf-8").toUpperCase();
                appMap.put("appId", shopInfo.getAppId());
                appMap.put("timeStamp", String.valueOf(timestamp));
                appMap.put("nonceStr", nonceStr);
                appMap.put("package", "prepay_id="+String.valueOf(resultJson.get("prepay_id")));
                appMap.put("signType", "MD5");
                String appSign = wechatSign(appMap, shopInfo.getMerchantSecret());

                map.put("appId", shopInfo.getAppId());
                map.put("timeStamp", String.valueOf(timestamp));
                map.put("nonceStr", nonceStr);
                map.put("package", "prepay_id="+String.valueOf(resultJson.get("prepay_id")));
                map.put("signType", "MD5");
                map.put("paySign", appSign);

                map.put("code", WechatPayUtil.Y);
                log.info(String.format("微信下单成功，用户：%s-%s， 订单：%s，价格：%s", user.getUserId(), user.getMobile(), orderId, price));
                return map;
            }
        }catch(Exception e){
            map.put("code", WechatPayUtil.N);
            map.put("message", "系统错误"+e.getMessage());
            log.error(String.format("微信下单失败，用户：%s-%s", user.getUserId(), user.getMobile()), e);
            return map;
        }
    }

    public static String wechatSign(Map<String, String> parameters, String wkey) {
        StringBuffer sb = new StringBuffer();
        Set<Map.Entry<String, String>> entrySet = parameters.entrySet();// 所有参与传参的参数按照accsii排序（升序）
        for (Map.Entry<String, String> entry : entrySet) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (null != value && !"".equals(value) && !"sign".equals(key) && !"key".equals(key)) {
                sb.append(key + "=" + value + "&");
            }
        }
        sb.append("key=" + wkey);
        String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
        return sign;
    }

    /**
     * 获取随机数
     * @param size
     * @return
     */
    public static String getRandomNumberString(int size){
        StringBuffer buffer = new StringBuffer(size);
        Random random = new Random();
        for(int i  = 0; i < size; i++){
            buffer.append(random.nextInt(10));
        }
        return buffer.toString();
    }

    public static String getXmlData(Map<String, String> map){
        StringBuffer result = new StringBuffer();
        if(map != null && !map.isEmpty()){
            result.append("<xml>");
            Set<Map.Entry<String,String>> entrySet = map.entrySet();
            for(Map.Entry<String,String> entry : entrySet){
                String key = entry.getKey();
                String value = entry.getValue();
                result.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
            }
            result.append("</xml>");
        }
        return result.toString();
    }

    public static JSONObject getJsonData(String xml){
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject = XML.toJSONObject(xml);
        } catch (JSONException e) {
            log.error("getJsonData 解析失败", e);
        }
        return jsonObject;
    }

    public static String setXML(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code
                + "]]></return_code><return_msg><![CDATA[" + return_msg
                + "]]></return_msg></xml>";
    }

    public static Map<String,Object> xmltoMap(String xml) {
        try {
            Map<String,Object> map = new HashMap<String,Object>();
            Document document = DocumentHelper.parseText(xml);
            Element nodeElement = document.getRootElement();
            List<Element>  node = nodeElement.elements();
            for (Element e : node)
                map.put(e.getName(), e.getText());
            node = null;
            nodeElement = null;
            document = null;
            return map;
        } catch (Exception e) {
            log.error("xmltoMap 解析失败", e);
        }
        return null;
    }


    /**
     * @功能 读取流 解析xml转为map
     * @throws Exception
     */
    public static Map<String, Object> readStream(HttpServletRequest request) throws Exception {
        InputStream inStream = request.getInputStream();

        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        String remess = new String(outSteam.toByteArray());
        log.info("XML报文内容为：" + remess);
        Map<String, Object> map = WechatPayUtil.xmltoMap(remess);
        return map;
    }
}
